#include <signal.h>
#include <unistd.h>

#include <ctime>
#include <iostream>

#include "currTime.h"
#include "itimerspecFromStr.h"

#define TIMER_SIG SIGRTMAX

using namespace std;

static void handler(int sig, siginfo_t* si, void* uc) {
    timer_t* tidPtr = static_cast<timer_t*>(si->si_value.sival_ptr);

    // UNSAFE: This handler uses non-async-signal-safe functions
    cout << "[" << currTime("%T") << "]"
         << " Got signal " << sig << endl;
    cout << "    *sival_ptr         = " << long(*tidPtr);
    cout << "    timer_getoverrun() = " << timer_getoverrun(*tidPtr);
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
        cout << argv[0] << " secs[/nsecs][:int-secs[/int-nsecs]...]" << endl;
        return 1;
    }

    timer_t* tidList = static_cast<timer_t*>(calloc(argc - 1, sizeof(timer_t)));
    if (tidList == nullptr) {
        perror("calloc");
        return 1;
    }

    // Establish handler for notification signal
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags     = SA_SIGINFO;
    sa.sa_sigaction = handler;
    if (sigaction(TIMER_SIG, &sa, nullptr) == -1) {
        perror("sigaction");
        return 1;
    }

    // Create and start one timer for each command-line argument
    struct sigevent sev;
    sev.sigev_notify = SIGEV_SIGNAL; // Notify via signal
    sev.sigev_signo  = TIMER_SIG;    // Notify using this signal

    // Current Time
    cout << "Now: " << currTime("%T") << endl;

    struct itimerspec ts;
    for (int i = 1; i < argc; i++) {
        itimerspecFromStr(argv[i], &ts);

        sev.sigev_value.sival_ptr =
            &tidList[i - 1]; // Allows handler to get ID of this timer

        if (timer_create(CLOCK_REALTIME, &sev, &tidList[i - 1]) == -1) {
            perror("timer_create");
            return 1;
        }
        cout << "Timer ID: " << long(tidList[i - 1]) << " (" << argv[i] << ")";

        if (timer_settime(tidList[i - 1], 0, &ts, nullptr) == -1) {
            perror("timer_settime");
            return 1;
        }
    }

    // Wait for incoming timer signals
    for (;;) {
        pause();
    }
}
